﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework;

namespace lumo.input
{
    /// <summary>
    /// Keypad class.
    /// </summary>
    public class Keypad : IUpdatableOnce
    {
        /// <summary>
        /// KeypadKey object
        /// </summary>
        class KeypadKey : IInputObject
        {
            private Keys     Key;
            private Keypad   KeypadParent;
            private bool     LastDown;
            private bool     CurrentDown;
            private float    RepeatCount = 0;
            private float   _RepeatDelay = Config.DefaultRepeatDelay;
            private float   _RepeatTime = Config.DefaultRepeatTime;

            // The propertie for frame ID
            private uint _FrameID = 0;
            /// <summary>Access for FrameID</summary>
            public uint FrameID { get { return _FrameID; } }

            /// <summary>
            /// Constructor for key.
            /// </summary>
            /// <param name="KeypadParent">The parent Keypad</param>
            /// <param name="Key">The key</param>
            public KeypadKey(Keypad KeypadParent, Keys Key)
            {
                this.KeypadParent = KeypadParent;
                this.Key          = Key;
                this.Repeated     = false;
                CurrentDown       = KeypadParent.State.IsKeyDown(Key);
                LastDown          = CurrentDown;
            }

            /// <summary>
            /// Update the key.
            /// </summary>
            /// <param name="Game">The game object (Not Needed)</param>
            public void Update(LumoComponent Game)
            {
                KeypadParent.Update(Game);
            }

            /// <summary>
            /// Internal update called on Keypad.Update
            /// </summary>
            public void InternalUpdate(LumoComponent Game)
            {
                // Only called once per frame.
                if (_FrameID == Game.FrameID)
                    return;
                _FrameID = Game.FrameID;

                LastDown = CurrentDown;
                CurrentDown = KeypadParent.State.IsKeyDown(Key);
                RepeatUpdate(Game);
            }

            /// <summary>
            /// Internal Repeat Update
            /// </summary>
            private void RepeatUpdate(LumoComponent Game)
            {
                Repeated = false;
                if (Game == null)
                {
                    Repeated = Triggered;
                    return;
                }

                if (CurrentDown)
                {
                    if (!LastDown)
                    {
                        Repeated = true;
                        RepeatCount = _RepeatDelay;
                    }
                    else
                    {
                        RepeatCount -= (float)Game.GameTime.ElapsedGameTime.TotalMilliseconds;
                        if (RepeatCount <= 0)
                        {
                            Repeated = true;
                            RepeatCount = _RepeatTime;
                        }
                    }
                }
            }

            /// <summary>
            /// Type of object.
            /// </summary>
            /// <returns>The type of object.</returns>
            public InputType GetInputType() { return InputType.Key; }

            /// <summary>
            /// Return the Key.
            /// </summary>
            /// <returns>The ID.</returns>
            public long GetInputID() { return (long)Key; }

            /// <summary>
            /// Implemented IInputObject.
            /// </summary>
            /// <returns>True if the key is pressed.</returns>
            public bool Pressed { get { return CurrentDown; } }

            /// <summary>
            /// Implemented IInputObject.
            /// </summary>
            /// <returns>True if the key is triggered.</returns>
            public bool Triggered { get { return (CurrentDown && !LastDown); } }

            /// <summary>
            /// Implemented IInputObject.
            /// </summary>
            /// <returns>True if the key is triggered.</returns>
            public bool Repeated { get; private set; }

            /// <summary>
            /// Implemented IInputObject Released.
            /// </summary>
            /// <returns>True if the key is released.</returns>
            public bool Released { get { return (LastDown && !CurrentDown); } }

            /// <summary>
            /// Implemented IInputObject Released.
            /// </summary>
            /// <returns>True if the key is released.</returns>
            public float RepeatDelay { get { return _RepeatDelay; } set { _RepeatDelay = value; } }
            
            /// <summary>
            /// Implemented IInputObject Released.
            /// </summary>
            /// <returns>True if the key is released.</returns>
            public float RepeatTime { get { return _RepeatTime; } set { _RepeatTime = value; } }
        }

        // Private variables
        private Dictionary<Keys, KeypadKey> KeypadKeyDictionary = new Dictionary<Keys, KeypadKey>();
        private KeyboardState   State;
        private PlayerIndex?    PlayerIndex;

        // The propertie for frame ID
        private uint _FrameID = 0;
        /// <summary>Access for FrameID</summary>
        public uint FrameID { get { return _FrameID; } }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="PlayerIndex">Index of the player.</param>
        public Keypad(PlayerIndex? PlayerIndex = null)
        {
            this.PlayerIndex = PlayerIndex;
            State = GetState();

            IEnumerable<Keys> AllKeys = Enum.GetValues(typeof(Keys)).Cast<Keys>();
            foreach (Keys key in AllKeys)
            {
                KeypadKeyDictionary.Add(key, new KeypadKey(this, key));
            }
        }

        /// <summary>
        /// Get the state of the current object.
        /// </summary>
        /// <returns>The state of the keypad or chatpad</returns>
        private KeyboardState GetState()
        {
            return (PlayerIndex.HasValue ? Keyboard.GetState(PlayerIndex.Value) : Keyboard.GetState());
        }

        /// <summary>
        /// Update the current keypad.
        /// </summary>
        /// <param name="key">The Key to Get.</param>
        public IInputObject GetKey(Keys key)
        {
            return KeypadKeyDictionary[key];
        }

        /// <summary>
        /// Get all Keys
        /// </summary>
        public IEnumerable<IInputObject> GetKeys()
        {
            return KeypadKeyDictionary.Values;
        }

        /// <summary>
        /// Update the current keypad.
        /// </summary>
        /// <param name="Game">The game object.</param>
        public void Update(lumo.LumoComponent Game)
        {
            // Only called once per frame.
            if (_FrameID == Game.FrameID)
                return;
            _FrameID = Game.FrameID;

            // Update the state.
            UpdateState();

            // Update every key
            foreach (KeypadKey key in KeypadKeyDictionary.Values)
            {
                key.InternalUpdate(Game);
            }
        }

        /// <summary>
        /// Update the state of current keypad.
        /// </summary>
        public void UpdateState()
        {
            // Save the state.
            State = GetState();
        }
    }
}
